#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

 

//char* SERVERIP = "127.0.0.1";
// char *N2NSERVERIP="151.11.50.180";//"supernode.ntop.org";
char *N2NSERVERIP="220.181.38.148";//"baidu.com";
#define N2NSERVERPORT 7777
char* HELLOWORD="hello\n";
char* remoteip="127.0.0.1";
int remoteport=0;
int localport=12345;

//////////static
static struct sockaddr_in lastpeer;
static char grecvbuf[257];
#define ERR_EXIT(m) \
    do \
{ \
    perror(m); \
    exit(EXIT_FAILURE); \
    } while(0)
static void SetLocalPort(int port){
	localport=port;
}
static int GetLocalPort(){
	return localport;
}
static void SetRemoteIP(char* str){
	remoteip=str;
}
static char* GetRemoteIP(){
	return remoteip;
}
static void SetRemotePort(int port){
	remoteport=port;
}
static int GetRemotePort(){
	return remoteport;
}
static void SetlastPeer(struct sockaddr_in *peeraddr){
	lastpeer=*peeraddr;
}
static struct sockaddr_in* GetLastPeer(){
	return &lastpeer;
}
static char* GetRecvBuff(){
	return grecvbuf;
}
static int GetRecvMax(){
	return sizeof(grecvbuf)-1;
}
static int GetTime(int t0){
	int t=time(NULL);
	return t-t0;
}
void printhex(unsigned char* buffer,int len){
	while(len>16){
		printhex(buffer,16);
		buffer+=16;
		len-=16;
	}
	for(int i=0;i<len;i++){
		fprintf(stderr,"%02x ",(unsigned char)buffer[i]);
	}
	for(int i=0;i<16-len;i++){
		fprintf(stderr,"   ");
	}
	for(int i=0;i<len;i++){
		switch(buffer[i]){
			case 0x00:
			case 0x09:
			case 0x0a:
			case 0x0d:
				fputc('.',stderr);
				break;
			default:
				fputc(buffer[i],stderr);
		}
	}
	fprintf(stderr,"\n");
}
static void print_recv(unsigned char* buffer,int len,struct sockaddr_in *peeraddr){
	fprintf(stderr,"从%s:%d接收到数据:\n",inet_ntoa(peeraddr->sin_addr),ntohs(peeraddr->sin_port));
	//printhex((char*)&peeraddr,peerlen);
	printhex(buffer,len);
	fprintf(stderr,"-------------\n");
}

static int issame(char* str1,char* str2){
	return !strcmp(str1,str2);
}
int recv_1(int sock){
	struct sockaddr_in peeraddr;
	socklen_t peerlen;
	char *recvbuf;
	int maxlen;
	int n;
	peerlen = sizeof(peeraddr);
	recvbuf=GetRecvBuff();
	maxlen=GetRecvMax();
	n = recvfrom(sock, recvbuf, maxlen, MSG_DONTWAIT,(struct sockaddr *)&peeraddr, &peerlen);
	if(n>0){
		recvbuf[n]=0;
		print_recv(recvbuf,n,&peeraddr);
		SetlastPeer(&peeraddr);
		return 1;
	}
	return 0;
}
void findhole(int sock){
	struct sockaddr_in servaddr;
	struct sockaddr_in servaddr0;
	char* ipaddr=GetRemoteIP();
	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(0);
	servaddr.sin_addr.s_addr = inet_addr(ipaddr);
	
	memset(&servaddr0, 0, sizeof(servaddr0));
	servaddr0.sin_family = AF_INET;
	servaddr0.sin_port = htons(N2NSERVERPORT);
	servaddr0.sin_addr.s_addr = inet_addr(N2NSERVERIP);
	sendto(sock, HELLOWORD, strlen(HELLOWORD), 0, (struct sockaddr *)&servaddr0, sizeof(servaddr0));
	sleep(1);
	for(int i=1;i<65535;i++){
		servaddr.sin_port = htons(i);
		sendto(sock, HELLOWORD, strlen(HELLOWORD), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
		usleep(1000);
		if(recv_1(sock)){
			struct sockaddr_in *peeraddr;
			char *recvbuf;
			peeraddr=GetLastPeer();
			if(peeraddr->sin_addr.s_addr==inet_addr(GetRemoteIP())){
				recvbuf=GetRecvBuff();
				if(issame(recvbuf,HELLOWORD)){
					SetRemotePort(ntohs(peeraddr->sin_port));
					fprintf(stdout,"%d\n",GetRemotePort());
					break;
				}
			}
		}
		if((i%1000)==0){
			sendto(sock, HELLOWORD, strlen(HELLOWORD), 0, (struct sockaddr *)&servaddr0, sizeof(servaddr0));
			usleep(1000);
		}
	}
	sleep(1);
	close(sock);
}
void shakehand(){
	int port;
	port=GetRemotePort();
	if(port){
		int sock;
		struct sockaddr_in servaddr;
		int t0,t;
		char temp[64];
		t0=GetTime(0);
		printf("shakehand port:%d\n",port);
		memset(&servaddr, 0, sizeof(servaddr));
		servaddr.sin_family = AF_INET;
		servaddr.sin_port = htons(port);
		servaddr.sin_addr.s_addr = inet_addr(GetRemoteIP());
		if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0){
			ERR_EXIT("socket");
		}
//		if (bind(sock, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0){
//			ERR_EXIT("bind error");
//		}
		while(1){
			t=GetTime(t0);
			sprintf(temp,"%s,%d",HELLOWORD,t);
			sendto(sock, temp, strlen(temp), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
			sleep(1);
			recv_1(sock);
			sleep(5);
		}
	}
}

//serverport clientport
int main(int argc,char* argv[])
{
	int sock;
	int keepalive=0;
	if(argc>=2){
		SetRemoteIP(argv[1]);
	}
	if(argc>=3){
		int port;
		sscanf(argv[2],"%d",&port);
		SetLocalPort(port);
	}
	if(argc>=4){
		keepalive=1;
	}
	if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0){
		ERR_EXIT("socket");
	}
	struct sockaddr_in servaddr;
	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(GetLocalPort());
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	if (bind(sock, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0){
		ERR_EXIT("bind error");
	}
	findhole(sock);
	if(keepalive)shakehand();
	//echo_cli(sock,ipaddr,sport);
	
	return 0;
}
